package com.fresh.bus.chat.business;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import javax.annotation.Resource;

import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.collections.DefaultRedisList;
import org.springframework.data.redis.support.collections.DefaultRedisSet;
import org.springframework.data.redis.support.collections.DefaultRedisZSet;
import org.springframework.data.redis.support.collections.RedisSet;
import org.springframework.data.redis.support.collections.RedisZSet;

import com.fresh.bus.canoe.message.Response;
import com.fresh.bus.canoe.session.Session;
import com.fresh.bus.canoe.session.SessionRoleManager;
import com.fresh.bus.chat.ctrl.OpCodes;
import com.fresh.bus.chat.domain.UserLog;
import com.fresh.bus.chat.persist.UserDao;
import com.fresh.bus.chat.persist.ZhiFeiJiDao;
import com.fresh.bus.chat.util.DateUtil;
import com.fresh.bus.chat.util.MathUtil;

public class ZhiFeiJiService implements InitializingBean {
	private static final Logger log = LoggerFactory
			.getLogger(ZhiFeiJiService.class);

	public static final short RANK_AMT = 5;

	public static final short IGNORE = 0;
	public static final short YES = 1;
	public static final short NO = 2;
	public static final short DEAD = 3;

	private static final short EXPIRED_DEFAULT_AMT = 10000;

	@Resource
	private ZhiFeiJiDao dao;
	@Resource
	protected SessionRoleManager roleManager;
	@Resource
	private UserService userService;
	@Resource
	private MessageService msgService;
	@Resource
	private UserDao userDao;
	@Resource
	private WealthService wealthService;
	@Resource
	private EnergyService energyService;	

	private StringRedisTemplate template;

	public RedisZSet<String> posts;
	public RedisZSet<String> weekFemalePosts;
	public RedisZSet<String> weekMalePosts;

	private ObjectMapper objectMapper = new ObjectMapper();
	private SimpleDateFormat dateformat1 = new SimpleDateFormat("yyyy-MM-dd");

	@Autowired
	public ZhiFeiJiService(StringRedisTemplate template) {
		this.template = template;
		posts = new DefaultRedisZSet<String>(KeyUtils.feijis(), template);
		weekFemalePosts = new DefaultRedisZSet<String>(KeyUtils.femaleFeijis(),
				template);
		weekMalePosts = new DefaultRedisZSet<String>(KeyUtils.maleFeijis(),
				template);
	}

	public RedisSet<String> myPost(String uid) {
		return new DefaultRedisSet<String>(KeyUtils.feijis(uid), template);
	}

	public void initZhiFeiJis() {
		long start = System.currentTimeMillis();
		String id = null;
		double score = 0.0;
		Date visitedAt = null;
		
		List<Map<String, Object>> posts = dao.getAllPosts();
		
		for (Map<String, Object> feiji : posts) {
			try {
				Map<String, String> data = dao.processData(feiji);
				createPostInMem(data);
				id = String.valueOf(feiji.get("id"));
				score = Double.valueOf((Double) feiji.get("score"));
				visitedAt = (Date)feiji.get("visited_at");
				score = visitedAt.getTime()/(1000 * 60);
				this.posts.add(id, score);
			} catch (Exception ex) {
				log.error("Create feiji failed:" + feiji.get("id"), ex);
				continue;
			}
		}
		log.info("Finish data inital " + (System.currentTimeMillis() - start)
				+ "ms");
	}

	public void initStarFeijis() {
		Date from = DateUtil.getNewDate(-30,0);
		Date to = DateUtil.getLastWeekLastDay();
		
		List<Map<String, Object>> males = dao.getStarPosts(from,to, "male");
		addStarFeiji(males,this.weekMalePosts);
		
		List<Map<String, Object>> females = dao.getStarPosts(from,to, "female");
		addStarFeiji(females,this.weekFemalePosts);
	}
	
	private void addStarFeiji(List<Map<String, Object>> posts,RedisZSet<String> container){
		String id = null;
		double score = 0.0;
		for (Map<String, Object> feiji : posts) {
			try {
				Map<String, String> data = dao.processData(feiji);
				createPostInMem(data);
				id = String.valueOf(feiji.get("id"));
				score = (Double) feiji.get("score");
				container.add(id, score);
			} catch (Exception ex) {
				log.error("Init star feiji failed:" + feiji.get("id"), ex);
				continue;
			}
		}
	}

	public String queryMyZhiFeiji(String user, Set<String> candidateList) {
		String key = null;
		Set<String> set;
		String replyUser = null;

		for (String id : candidateList) {
			key = KeyUtils.fid("*", user, id, "*");
			set = template.keys(key);
			if (!set.isEmpty()) {
				key = set.iterator().next();
				replyUser = id;
				break;
			}

			key = KeyUtils.fid("*", id, user, "*");
			set = template.keys(key);
			if (!set.isEmpty()) {
				key = set.iterator().next();
				replyUser = id;
				break;
			}
		}

		if (replyUser != null) {
			BoundHashOperations<String, String, String> ops = template
					.boundHashOps(key);
			if (ops.get("status").equals("1")) {
				updateStatus(key, "2");

				String msg = "太棒了！飞机传情意，我们见面了！";
				/*
				this.msgService.notifyTargetUser(replyUser,
						MessageService.MEET_FROM_FEIJI, msg);
				this.msgService.notifyTargetUser(user,
						MessageService.MEET_FROM_FEIJI, msg);
				*/

			} else {
				replyUser = null;
			}
		}
		return replyUser;
	}

	private void updateStatus(String key, String status) {
		String pid = this.key2dbId(key);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(key);
		ops.put("status", status);
		dao.updatePost(pid, ops.entries());

		String replyUser = ops.get("replyUser");
		String fromUser = ops.get("fromUser");

		String uid;
		BoundHashOperations<String, String, String> uop;
		Session session;

		if (status.equals("-1")) {
			double score = Double.valueOf(ops.get("score"));
			score -= EXPIRED_DEFAULT_AMT;
			this.posts.add(pid, score);
			// 重置搭乘人状态
			if (!replyUser.equals("0")) {
				uid = KeyUtils.uid(replyUser);
				uop = template.boundHashOps(uid);
				uop.put("receiveFeiji", "");
				userService.saveUser(replyUser);
				session = this.roleManager.getSession(replyUser);
				if (session != null) {
					userService.synchroUser(session);
				}
			}

			// 计算个人历史纪录
			uid = KeyUtils.uid(fromUser);
			uop = template.boundHashOps(uid);
			double loc1 = 0.0;
			if (uop.get("maxHeight") != null
					&& uop.get("maxHeight").length() > 0) {
				loc1 = Double.valueOf(uop.get("maxHeight"));
			}
			if (score > loc1) {
				uop.put("maxHeight", String.valueOf(score));
				userService.saveUser(fromUser);
				session = this.roleManager.getSession(fromUser);
				if (session != null) {
					userService.synchroUser(session);
				}
			}
			String msg = "提示 ：纸飞机已着陆。";
			//msgService.notifyTargetUser(fromUser, MessageService.ARRIVAL_FEIJI,msg);
		}
	}

	// 评测明星飞机
	private boolean isStarFeiji(String pid) {
		Set<String> set;
		String key = this.dbId2Key(pid);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(key);
		double score = Double.valueOf(ops.get("score"));
		if (ops.get("sex").equals("female")) {
			set = this.weekFemalePosts.reverseRangeByScore(Double.MIN_VALUE,
					score);
			if (!set.isEmpty()) {
				this.weekFemalePosts.add(pid, score);
				this.updateLevel(key, "1");
				if (weekFemalePosts.size() > ZhiFeiJiService.RANK_AMT)
					weekFemalePosts.remove(weekFemalePosts.first());
				return true;
			}
		} else {
			set = this.weekMalePosts.reverseRangeByScore(Double.MIN_VALUE,
					score);
			if (!set.isEmpty()) {
				this.weekMalePosts.add(pid, score);
				this.updateLevel(key, "1");
				if (weekMalePosts.size() > ZhiFeiJiService.RANK_AMT)
					weekMalePosts.remove(weekMalePosts.first());
				return true;
			}
		}
		return false;
	}

	private void updateLevel(String key, String lev) {
		String[] loc = key.split(":");
		String pid = loc[1];
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(key);
		ops.put("level", lev);
		dao.updatePost(pid, ops.entries());

		String fromUser = ops.get("fromUser");
		if (lev.equals("1")) {
			String msg = "太棒了！因为卓越表现，您的纸飞机已晋升为本周明星飞机！";
			//msgService.notifyTargetUser(fromUser,MessageService.ADD_STAR_FEIJI, msg);
		}
	}

	private String dbId2Key(String dbId) {
		return template.keys(KeyUtils.fid(dbId, "*", "*", "*")).iterator()
				.next();
	}

	private String key2dbId(String key) {
		return key.split(":")[1];
	}

	private void destroy(String key) {
		String pid = key2dbId(key);
		template.delete(key);
		this.posts.remove(pid);
		dao.deletePost(pid);
	}

	@Override
	public void afterPropertiesSet() {
	}

	public void doIt() {
		try {
			Calendar calendar = Calendar.getInstance();
			int y = calendar.get(Calendar.YEAR);
			int m = calendar.get(Calendar.MONTH);
			int d = calendar.get(Calendar.DAY_OF_MONTH);
			int h = calendar.get(Calendar.HOUR_OF_DAY);
			calendar.set(y, m, d, h, 0, 0);
			calendar.set(Calendar.MILLISECOND, 0);
			Date now = calendar.getTime();

			SimpleDateFormat date_format = new SimpleDateFormat(
					"yyyy-MM-dd HH:mm:ss");
			String getDate = date_format.format(now);
			log.info("Current exp_date is " + getDate);

			List<Map<String, Object>> checkList = (List<Map<String, Object>>) dao
					.getPosts(now, 1);
			for (Map<String, Object> post : checkList) {
				try {
					validTime(post);
				} catch (Exception ex) {
					log.error("Error occur when valid post:" + post.get("id"),
							ex);
					continue;
				}
				log.warn("Finish valid feiji:[" + post.get("id") + "]");
			}

			checkList = (List<Map<String, Object>>) dao.getPosts(now, 0);
			for (Map<String, Object> post : checkList) {
				try {
					archiveFeiji(post);
				} catch (Exception ex) {
					log.error(
							"Error occur when archive post:" + post.get("id"),
							ex);
					continue;
				}
				log.warn("Finish archive feiji:[" + post.get("id") + "]");
			}

		} catch (Exception e) {
			log.error("Some error occur...", e);
		}
	}

	// 校验是否守约
	private void validTime(Map<String, Object> post) throws Exception {
		Session session;
		String user;
		String date = String.valueOf(post.get("expDate"));
		if (post.get("fromCome") == null) {
			user = String.valueOf(post.get("fromUser"));
			session = this.roleManager.getSession(user);
			if (session != null) {
				wealthService.update(1, WealthService.FEIJI_YUEDING, user);
			}
		}
		if (post.get("replyCome") == null) {
			user = String.valueOf(post.get("replyUser"));
			session = this.roleManager.getSession(user);
			if (session != null) {
				wealthService.update(1, WealthService.FEIJI_YUEDING, user);
			}
		}
	}

	// 归档过期的纸飞机
	private void archiveFeiji(Map<String, Object> post) {
		String from = String.valueOf(post.get("fromUser"));
		String date = String.valueOf(post.get("expDate"));

		String pid = String.valueOf(post.get("id"));
		String key = template.keys(KeyUtils.fid(pid, "*", "*", "*")).iterator()
				.next();
		this.updateStatus(key, "-1");
	}

	// 每周重置加星飞机
	public void clearFeiji() {
		this.posts.removeAll(this.posts);
		this.initZhiFeiJis();

		this.weekMalePosts.removeAll(this.weekMalePosts);
		this.weekFemalePosts.removeAll(this.weekFemalePosts);
		this.initStarFeijis();

		// 奖励美誉度
		BoundHashOperations<String, String, String> ops;
		String pid;
		String user;
		for (String dbId1 : this.weekMalePosts) {
			pid = this.dbId2Key(dbId1);
			ops = template.boundHashOps(pid);
			user = ops.get("fromUser");
			this.wealthService.update(1, WealthService.WEEK_STAR_FEIJI, user);
			msgService.notifyTargetUser(user,"",UserLog.ADD_STAR_FEIJI, "0");
		}

		for (String dbId2 : this.weekFemalePosts) {
			pid = this.dbId2Key(dbId2);
			ops = template.boundHashOps(pid);
			user = ops.get("fromUser");
			this.wealthService.update(1, WealthService.WEEK_STAR_FEIJI, user);
			msgService.notifyTargetUser(user,"",UserLog.ADD_STAR_FEIJI, "0");
		}
	}

	public Map<String,String> findPost(long start, String user) {
		Set<String> set = this.posts.reverseRange(start, -1);
		String fid = null;
		Map<String,String> m = null;
		String visiters;
		BoundHashOperations<String, String, String> ops;

		for (String id : set) {
			String pid = template.keys(KeyUtils.fid(id, "*", "*", "*"))
					.iterator().next();
			ops = template.boundHashOps(pid);
			visiters = ops.get("ignoreUsers") + ops.get("yesUsers")
					+ ops.get("noUsers");
			start ++;
			if (visiters.indexOf(user) == -1) {
				fid = id;
				m = this.readPost(fid);
				m.put("postIndex", String.valueOf(start));
				break;
			}
		}
		return m;
	}

	public Map<String, String> readPost(String pid) {
		pid = KeyUtils.fid(pid, "*", "*", "*");
		pid = template.keys(pid).iterator().next();
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(pid);
		String msg = ops.get("expDate");
		Date expDate = DateUtil.convertStringToDate(msg);
		int i = DateUtil.getDaysBetween(new Date(), expDate);
		Calendar cal = Calendar.getInstance();
		cal.setTime(expDate);
		int h = cal.get(Calendar.HOUR_OF_DAY);
		switch (i) {
		case 0:
			msg = "今天" + h + "点";
			break;
		case 1:
			msg = "明天" + h + "点";
			break;
		default:
			msg = dateformat1.format(expDate);
			break;
		}
		ops.put("expDateStr", msg);
		return ops.entries();
	}

	public List<Map<String, String>> readPostByUser(String uid) {
		List<Map<String, String>> feijis = new ArrayList<Map<String, String>>();
		Map<String, String> m;
		try {
			List<Map<String, Object>> data = dao.getPostByUser(uid);
			for (Map<String, Object> o : data) {
				m = dao.processData(o);
				feijis.add(m);
			}
			return feijis;
		} catch (Exception ex) {
			log.error("User id is " + uid, ex.getMessage());
			return null;
		}
	}

	// 制作纸飞机
	public boolean createPost(Map<String, Object> map, String from,boolean isFromSystem){
		Map<String,String> u = userService.findUser(from);
		int permitAmt = u.get("permitFeiji") == null ? UserService.DAILY_FEIJI_AMT : Integer
				.valueOf(u.get("permitFeiji"));
		if(permitAmt<=0) return false;
		
		String content = (String) map.get("content");
		String sex = u.get("sex");
		map.put("sex", sex);
		map.put("created_user", from);

		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.DATE, 7);
		Date date = calendar.getTime();
		Date now = new Date();

		String initIgnoreUsers = "[\"" + from + "\"]";
		double score = initScore(from);
		map.put("score", String.valueOf(score));

		int dbId = dao.addPost(content, sex, date, 0, from, now,
				initIgnoreUsers, score);
		String id = String.valueOf(dbId);
		map.put("id", dbId);
		map.put("exp_date", date);
		map.put("status", 0);
		map.put("level", 0);
		map.put("created_at", now);
		map.put("ignore_users", initIgnoreUsers);
		map.put("yes_users", "[]");
		map.put("no_users", "[]");

		Map<String, String> data = dao.processData(map);
		createPostInMem(data);
		
		long newscore = now.getTime()/(60*1000);
		this.posts.add(id, newscore);
		
		if (this.posts.size() > 200) {
			String rid = this.posts.first();
			this.posts.remove(rid);
			String key = this.dbId2Key(rid);
			template.delete(key);
		}
		
		if(!isFromSystem){
			// 同步用户
			BoundHashOperations<String, String, String> o = template
					.boundHashOps(KeyUtils.uid(from));
			
			o.put("selfNotice", content);
			o.put("sendFeiji", id);
			permitAmt--;
			o.put("permitFeiji", String.valueOf(permitAmt));

			//更新体力值
			//energyService.update(from, EnergyService.WRITE_FEIJI_ENERGY);
			//expService.update(session, data, OpCodes.WRITE_FEIJI);
		}
		return true;
	}

	private double getScore(String user, double baseScore) {
		double levelRate = 0.02; // 级别加权
		double incomeRate = 0.02; // 美誉加权

		BoundHashOperations<String, String, String> up = template
				.boundHashOps(KeyUtils.uid(user));

		int level = Integer.valueOf(up.get("level"));
		int income = Integer.valueOf(up.get("income"));

		double score = baseScore
				* (1 + levelRate * level + incomeRate * income);

		return score;
	}

	private double initScore(String fid) {
		// 计算score
		int baseScore = 5; // 基础值
		double levelRate = 0.2; // 级别加权
		double incomeRate = 1.0; // 美誉加权

		BoundHashOperations<String, String, String> up = template
				.boundHashOps(KeyUtils.uid(fid));

		int level = Integer.valueOf(up.get("level"));
		int income = Integer.valueOf(up.get("income"));

		double score = baseScore + levelRate * level + incomeRate * income;
		score = MathUtil.round(score, 2);
		return score;
	}

	@SuppressWarnings("unchecked")
	public void updateScore(String fid, int actionType, String user) {
		Set<String> usersSet = null;
		String usersStr = null;

		String pid = KeyUtils.fid(fid, "*", "*", "*");
		pid = template.keys(pid).iterator().next();
		BoundHashOperations<String, String, String> op = template
				.boundHashOps(pid);
		double score = Double.valueOf(op.get("score"));

		try {
			switch (actionType) {
			case ZhiFeiJiService.IGNORE:
				usersSet = objectMapper.readValue(op.get("ignoreUsers"),
						Set.class);
				usersSet.add(user);
				usersStr = objectMapper.writeValueAsString(usersSet);
				op.put("ignoreUsers", usersStr);
				op.put("ignoreAmt", String.valueOf(usersSet.size()));
				break;
			case ZhiFeiJiService.YES:
				usersSet = objectMapper
						.readValue(op.get("yesUsers"), Set.class);
				usersSet.add(user);
				usersStr = objectMapper.writeValueAsString(usersSet);
				op.put("yesUsers", usersStr);
				op.put("yesAmt", String.valueOf(usersSet.size()));
				score += getScore(user, 2.0);
				Date now = new Date();
				double newscore = now.getTime()/(1000 * 60);
				posts.add(fid, newscore);
				break;
			case ZhiFeiJiService.NO:
				usersSet = objectMapper.readValue(op.get("noUsers"), Set.class);
				usersSet.add(user);
				usersStr = objectMapper.writeValueAsString(usersSet);
				op.put("noUsers", usersStr);
				op.put("noAmt", String.valueOf(usersSet.size()));
				score += getScore(user, -2.0);
				break;
			}
		} catch (Exception e) {
			log.error("Error occur in:" + fid + "/" + actionType + "/" + user,
					e);
		}
		score = MathUtil.round(score, 2);
		// 更新数据库
		op.put("score", String.valueOf(score));
		dao.updateUsers(actionType, usersStr, fid, score);
	}

	// 内存中创建飞机内容
	private void createPostInMem(Map<String, String> map){
		String id = map.get("id");
		String from = map.get("fromUser");
		String to = map.get("replyUser");
		String dateStr = map.get("createdAt");

		String pid = KeyUtils.fid(id, from, to, dateStr);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(pid);
		ops.putAll(map);
	}

	// 收取飞机
	public void getFeiji(Map<String, Object> map, String user) throws Exception {
		String postId = (String) map.get("id");
		String from = (String) map.get("from");
		String date = (String) map.get("date");

		String p1 = KeyUtils.fid(postId, from, "0", date);

		if (this.template.keys(p1).size() <= 0) {
			throw new Exception("飞机已被他人收取");
		}

		if (this.template.keys(KeyUtils.fid("*", "*", user, date)).size() > 1) {
			throw new Exception("已超过可收当日飞机数量");
		}

		if (this.template.keys(KeyUtils.fid("*", user, from, "*")).size() > 0
				|| this.template.keys(KeyUtils.fid("*", from, user, "*"))
						.size() > 0) {
			throw new Exception("双方已经有约定了!");
		}

		String p2 = KeyUtils.fid(postId, from, user, date);
		template.rename(p1, p2);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(p2);
		ops.put("replyUser", user);
		ops.put("status", "1");
		dao.updatePost(postId, ops.entries());

		String msg = "用户" + user + "已收取<font color='#FF0000'>"
				+ (String) ops.get("expDate") + "</font>的纸飞机。";

		//msgService.notifyTargetUser(from, MessageService.GET_FEIJI, msg);

		// 设定飞机
		String uid = KeyUtils.uid(user);
		BoundHashOperations<String, String, String> uop = template
				.boundHashOps(uid);
		uop.put("receiveFeiji", postId);

		Session session = this.roleManager.getSession(user);
		userService.synchroUser(session);
	}

	public Map<String, Object[]> getStarFeiji() {
		Map<String, Object[]> map = new HashMap<String, Object[]>();

		List<Map<String, String>> mlist = new ArrayList<Map<String, String>>();
		BoundHashOperations<String, String, String> ops;
		Set<String> set = this.weekMalePosts.reverseRange(0, -1);
		for (String mid : set) {
			ops = template.boundHashOps(this.dbId2Key(mid));
			mlist.add(ops.entries());
		}
		map.put("males", mlist.toArray());

		List<Map<String, String>> flist = new ArrayList<Map<String, String>>();
		set = this.weekFemalePosts.reverseRange(0, -1);
		for (String fid : set) {
			ops = template.boundHashOps(this.dbId2Key(fid));
			flist.add(ops.entries());
		}
		map.put("females", flist.toArray());
		return map;
	}

	public List<Map<String, String>> getYesFeijiPeople(String fid)
			throws Exception {
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();
		Map<String, Object> m = dao.getPost(fid);
		String yesUsers = (String) m.get("yes_users");
		Set<String> usersSet = objectMapper.readValue(yesUsers, Set.class);
		for (String id : usersSet) {
			list.add(userService.findUser(id));
		}
		return list;
	}
}
